package main

import (
	"context"
	"time"

	"github.com/kataras/iris/v12"
	//"github.com/kataras/iris/v12/middleware/logger"
	//"github.com/kataras/iris//v12/middleware/recover"
	"github.com/kataras/iris/v12/mvc"
	"github.com/kataras/iris/v12/sessions"
	"iris-example/controller"
	"iris-example/tools"
)

var dirOptions = iris.DirOptions{
	// 默认index.html 映射为“/index.html“
	//IndexName: "/index.html",// 不指定或指定无效则默认渲染目录中的文件

	// 压缩
	Compress: false,
	//如果没有找到"IndexName",是否列出当前请求目录中的文件
	ShowList: true,
	Cache: iris.DirCacheOptions{
		// 启用内存缓存并预压缩文件.
		Enable: true,
		// 忽略图像类型（和pdf）.
		CompressIgnore: iris.MatchImagesAssets,
		// 不要压缩小于设定大小的文件.
		CompressMinSize: 30 * iris.B,
		// 可根据客户需要协商的可用编码.
		Encodings: []string{"gzip", "br" /* you can also add: deflate, snappy */},
		Verbose:   1,
	},

	// 如果“ShowList”为true，则将使用此函数而不是默认值
	DirList: iris.DirListRich(), // 显示当前请求目录（dir）的文件列表
	//DirList: func(ctx iris.Context, dirName string, dir http.File) errors { ... }

	// 可选的验证器，循环通过每个请求的资源.
	// AssetValidator:  func(ctx iris.Context, name string) bool { ... }
}

// 构建App
// @returns *iris.Application
func newApp(staticPath string) *iris.Application {
	// 1.创建app结构体对象
	app := iris.New()

	// 优雅关机
	iris.RegisterOnInterrupt(func() {
		timeout := 5 * time.Second
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()
		// 关闭所有主机
		_ = app.Shutdown(ctx)
	})

	//app.Use(recover.New())
	//app.Use(logger.New())

	app.Logger().SetLevel("debug")

	// This will serve the ./public/favicon/favicon.ico to: localhost:8080/favicon.ico
	// @params (系统路径, 图标别名-比如区分设备的图标-多个favicon（桌面，移动设备等）)
	app.Favicon("./public/favicon.ico", /*默认值: "/favicon.ico"*/)

	// 使用`_ = ctx.View("index.html")`=》view engine is missing, use `RegisterView`；通过HandleDir解析的静态资源不支持
	app.RegisterView(iris.HTML("./public/views", ".html").Reload(true))

	// @params (请求路径, 系统目录)
	// app.HandleDir("/css", iris.Dir("./assets/css"))
	// app.HandleDir("/js",  iris.Dir("./assets/js"))
	app.HandleDir("/", iris.Dir("./public/upload"), dirOptions)

	return app
}

/**
 * 项目设置：字符编码+错误监听处理
 */
func configuration(app *iris.Application) {
	//配置 字符编码
	app.Configure(iris.WithConfiguration(iris.Configuration{
		DisableStartupLog:                 false,
		DisableInterruptHandler:           false,
		DisablePathCorrection:             false,
		EnablePathEscape:                  false,
		FireMethodNotAllowed:              false,
		DisableBodyConsumptionOnUnmarshal: false,
		DisableAutoFireStatusCode:         false,
		TimeFormat:                        "Mon, 02 Jan 2006 15:04:05 GMT",
		Charset:                           "UTF-8",
	}))

	//错误配置 - 路由not found未匹配：未发现错误
	app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
		// 出现 404 的时候，就跳转到 $views_dir/errors/404.html 模板
		ctx.View("errors/404.html")
	})

	// 500 为特定HTTP错误注册自定义处理程序方法
	// 当出现StatusInternalServerError内部错误返回500响应
	app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) {
		// ctx.Values() 是一个很有用的东西，主要用来使 处理方法与中间件 通信 记住真的很重要
		// ctx.Values().GetString("errors") 获取自定义错误提示信息
		errMessage := ctx.Values().GetString("errors")
		if errMessage != "" {
			// ctx.Writef("Internal server errors: %s", errMessage)
			_, _ = ctx.JSON(iris.Map{
				"status": iris.StatusInternalServerError,
				"msg":    "integral errors",
				"errMsg": errMessage,
			})
			return
		}
		_, _ = ctx.JSON(iris.Map{
			"status": iris.StatusInternalServerError,
			"msg":    "integral errors",
			"errMsg": "(Unexpected) internal server errors",
		})
	})
}

/**
 * MVC `路由配置`架构模式处理
 */
func mvcHandle(app *iris.Application) {
	//启用session
	sessManager := sessions.New(sessions.Config{
		Cookie:  "sessionCookie",
		Expires: 24 * time.Hour,
	})
	app.Use(sessManager.Handler())

	//engine := datasource.NewMysqlEngine()

	//管理员模块功能
	//adminService := service.NewAdminService(engine)

	// ["Get", "Post", "Put", "Delete", "Options", "Trace", "Connect", "Head", "Patch", "Any"]
	controller.Handlers(app)
	// Party-分组路由
	//app.PartyFunc("/cpanel", func(child iris.Party) {
	//	child.Use(xxxMiddlewareHandler)
	//	child.Get("/", func(ctx iris.Context){})
	//})
	// 或者
	v1 := app.Party("/v1", /* xxxMiddlewareHandler */)
	{
		// You can also register any index handler manually, order of registration does not matter:
		//v1.Get("/staticPath", [...custom middleware...], func(ctx iris.Context) {
		// [...custom code...]
		//	ctx.ServeFile("./assets/index.html")
		//})

		v1.Get("/", func(ctx iris.Context) {
			//ctx.ViewData("Page", page)         // page为结构体
			_ = ctx.View("index.html") // 可配合ViewData使用达到传递数据

			//或者ctx.ServeFile("index.html", false) // 按原样渲染视图(不显示，没有动态数据)
		}).Name = "v1首页"

		v1.Get("/{id:int}", func(ctx iris.Context) {
			// 一个路径参数名称应该只包含小写字母，像 _ 和数字这样的符号是 不允许的
			id, _ := ctx.Params().GetInt("id") // 路径参数值
			//ctx.Values().Get("name")           //上下文中用于处理器之间通信的局部存储和中间件
			_, _ = ctx.HTML("<h1>Welcome</h1>")
			_, _ = ctx.WriteString("返回string")
			_, _ = ctx.JSON(iris.Map{"message": "Hello Iris!", "id": id})
		})

		// example 组
		// 连写：mvc.New(app).Handle(new(controller.ExampleController))
		example := mvc.New(v1.Party("/example"))
		// admin.Register(
		// 	adminService,
		// 	sessManager.Start,
		// )// 注册服务，session
		example.Handle(new(controller.ExampleController))
	}
}

func main() {
	// 通过tools读取程序默认json配置信息
	config, err := tools.InitConfig("./config/config.json")
	if err != nil {
		//tools.LogError(err)
	}

	// 调用newApp构建App
	app := newApp(config.StaticPath)

	// 应用App设置
	configuration(app)

	// 路由设置
	mvcHandle(app)

	// 监听端口 - 有返回值,但是未使用,_接收
	addr := ":" + config.Port
	//errorRun := app.Listen(":7006")

	// 文件配置：
	// 当你有两份不同的配置时这样使用，一份用于开发，一份用于生产
	// app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.TOML("./configs/iris.tml")))
	// app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.YAML("./configs/iris.yml")))

	err = app.Run(
		iris.Addr(addr),                               // 在端口9000进行监听
		iris.WithoutServerError(iris.ErrServerClosed), // 无服务错误提示
		iris.WithOptimizations,                        // 对json数据序列化更快的配置
		iris.WithoutInterruptHandler,                  // 为了手动管理应用程序中断时要执行的操作，我们必须使用选项「WithoutInterruptHandler」来禁用默认行为并注册新的中断处理程序（全局，跨所有可能的主机）
	)
	if err != nil {
		panic(err)
	}
}
